Building Dynamic UI from Lists in Flutter
Building Dynamic UI from Lists is an important Flutter concept. Instead of manually creating every widget, you can store application data inside a Dart List and generate Flutter widgets from that data. This approach is useful for displaying products, students, users, messages, categories, notifications, tasks, orders, and API results.
Flutter provides widgets such as ListView and ListView.builder for displaying collections of data. For large or dynamic lists, ListView.builder creates list items as they are needed while scrolling, which avoids creating every item at once. :contentReference[oaicite:0]{index=0}
1. What is Dynamic UI?
A dynamic UI is an interface whose content is generated from data rather than being completely hard-coded.
For example, instead of writing three separate widgets:
Column(
children: const [
Text('Apple'),
Text('Banana'),
Text('Mango'),
],
)
you can store the data in a List:
final fruits = [
'Apple',
'Banana',
'Mango',
];
Then generate the UI from the List:
Column(
children: fruits.map((fruit) {
return Text(fruit);
}).toList(),
)
This makes the UI easier to maintain and allows the number of displayed items to change automatically when the data changes.
2. Why Build UI from Lists?
- Reduces repetitive UI code.
- Makes applications data-driven.
- Makes it easier to display API and database data.
- Allows the number of UI items to change dynamically.
- Makes filtering and searching easier.
- Works well with Flutter's builder widgets.
- Supports large collections more efficiently with lazy builders.
- Makes reusable UI components easier to create.
3. Creating a Simple Data List
The first step is to create a collection containing the data that will be displayed.
final List fruits = [
'Apple',
'Banana',
'Mango',
'Orange',
'Grapes',
];
Here, each string represents one item in the UI.
4. Creating Dynamic Widgets with map()
Dart's map() method can transform each List item into a Flutter widget.
final fruits = [
'Apple',
'Banana',
'Mango',
];
final widgets = fruits.map((fruit) {
return Text(fruit);
}).toList();
The resulting List contains Flutter Text widgets.
Displaying the Widgets
Column(
children: fruits.map((fruit) {
return Text(fruit);
}).toList(),
)
5. Dynamic List with ListView
When content needs to scroll, Flutter provides the ListView widget. The basic ListView constructor is suitable for relatively small collections. :contentReference[oaicite:1]{index=1}
final fruits = [
'Apple',
'Banana',
'Mango',
'Orange',
];
ListView(
children: fruits.map((fruit) {
return ListTile(
title: Text(fruit),
);
}).toList(),
)
Each List item is converted into a ListTile.
6. Understanding ListView.builder
ListView.builder is one of the most important tools for creating dynamic lists in Flutter. It receives an item count and an itemBuilder callback that creates the widget for a particular index. Flutter's documentation recommends it for long or dynamic lists because items are built as they become necessary during scrolling. :contentReference[oaicite:2]{index=2}
final fruits = [
'Apple',
'Banana',
'Mango',
'Orange',
];
ListView.builder(
itemCount: fruits.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(fruits[index]),
);
},
)
Important Properties
| Property | Purpose |
| itemCount | Specifies the number of items. |
| itemBuilder | Builds the widget for each index. |
| index | Identifies the current position in the data List. |
| scrollDirection | Controls vertical or horizontal scrolling. |
| physics | Controls scrolling behavior. |
| padding | Adds space around the list. |
| shrinkWrap | Controls whether the list sizes itself to its contents. |
7. Complete Dynamic List Example
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const ProductPage(),
);
}
}
class ProductPage extends StatelessWidget {
const ProductPage({super.key});
final List products = const [
'Laptop',
'Mobile Phone',
'Tablet',
'Headphones',
'Keyboard',
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Products'),
),
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
leading: const Icon(Icons.shopping_bag),
title: Text(products[index]),
trailing: const Icon(Icons.arrow_forward_ios),
);
},
),
);
}
}
8. Understanding the index
The index parameter tells you which data item is currently being built.
ListView.builder(
itemCount: fruits.length,
itemBuilder: (context, index) {
print(index);
print(fruits[index]);
return ListTile(
title: Text(fruits[index]),
);
},
)
If the List contains five items, the indexes are:
| Index | Data |
| 0 | Apple |
| 1 | Banana |
| 2 | Mango |
| 3 | Orange |
| 4 | Grapes |
9. Dynamic UI with List of Objects
For real-world applications, using a List of model objects is usually more useful than using a List of simple strings.
class Product {
final String name;
final double price;
final String category;
Product({
required this.name,
required this.price,
required this.category,
});
}
Now create a List of products:
final List products = [
Product(
name: 'Laptop',
price: 55000,
category: 'Electronics',
),
Product(
name: 'Phone',
price: 25000,
category: 'Electronics',
),
Product(
name: 'Chair',
price: 5000,
category: 'Furniture',
),
];
10. Displaying Objects in ListView.builder
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
subtitle: Text(product.category),
trailing: Text('₹${product.price}'),
);
},
)
This approach separates the data model from the UI and makes the application easier to maintain.
11. Dynamic Product Cards
Dynamic UI does not have to use only ListTile. Each List item can return almost any widget structure.
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
margin: const EdgeInsets.all(8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(product.category),
const SizedBox(height: 8),
Text('₹${product.price}'),
],
),
),
);
},
)
12. Adding Images Dynamically
Image URLs can also be stored inside a List and displayed dynamically.
class Product {
final String name;
final double price;
final String imageUrl;
Product({
required this.name,
required this.price,
required this.imageUrl,
});
}
Example data:
final products = [
Product(
name: 'Laptop',
price: 55000,
imageUrl: 'https://example.com/laptop.jpg',
),
Product(
name: 'Phone',
price: 25000,
imageUrl: 'https://example.com/phone.jpg',
),
];
Display the image dynamically:
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
leading: Image.network(
product.imageUrl,
width: 50,
height: 50,
fit: BoxFit.cover,
),
title: Text(product.name),
subtitle: Text('₹${product.price}'),
);
},
)
13. Dynamic UI from API-Style Data
Application data frequently comes from a server or database. A simple JSON-like structure can be represented using a List of Maps.
final List
Display the data dynamically:
ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
leading: const CircleAvatar(
child: Icon(Icons.person),
),
title: Text(user['name']),
subtitle: Text(user['email']),
trailing: Text(user['role']),
);
},
)
14. Dynamic UI with List.generate()
Dart's List.generate() can create a collection programmatically. This is useful for sample data, test data, numbered items, and repeated UI structures.
final items = List.generate(
10,
(index) => 'Item ${index + 1}',
);
Display them:
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
)
15. Dynamic UI with Conditional Data
You can filter data before displaying it.
final products = [
Product(name: 'Laptop', price: 55000, category: 'Electronics'),
Product(name: 'Phone', price: 25000, category: 'Electronics'),
Product(name: 'Chair', price: 5000, category: 'Furniture'),
];
final electronics = products
.where((product) => product.category == 'Electronics')
.toList();
Now only the filtered products can be displayed:
ListView.builder(
itemCount: electronics.length,
itemBuilder: (context, index) {
final product = electronics[index];
return ListTile(
title: Text(product.name),
subtitle: Text('₹${product.price}'),
);
},
)
16. Dynamic Search UI
A common application feature is searching through a List and updating the displayed UI according to the search text.
class ProductSearchPage extends StatefulWidget {
const ProductSearchPage({super.key});
@override
State createState() =>
_ProductSearchPageState();
}
class _ProductSearchPageState
extends State {
final List products = [
'Laptop',
'Mobile Phone',
'Tablet',
'Keyboard',
'Mouse',
'Headphones',
];
String searchText = '';
@override
Widget build(BuildContext context) {
final filteredProducts = products
.where(
(product) => product
.toLowerCase()
.contains(searchText.toLowerCase()),
)
.toList();
return Scaffold(
appBar: AppBar(
title: const Text('Product Search'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: TextField(
decoration: const InputDecoration(
hintText: 'Search products',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
),
onChanged: (value) {
setState(() {
searchText = value;
});
},
),
),
Expanded(
child: ListView.builder(
itemCount: filteredProducts.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(filteredProducts[index]),
);
},
),
),
],
),
);
}
}
Whenever the user enters new text, setState() updates the search value and Flutter rebuilds the relevant UI.
17. Dynamic UI with Categories
Categories can also be stored in a List and displayed dynamically.
final categories = [
'All',
'Electronics',
'Furniture',
'Clothing',
'Books',
];
Example horizontal category list:
SizedBox(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6,
),
child: Chip(
label: Text(categories[index]),
),
);
},
),
)
18. Dynamic UI with Buttons
Lists can be used to generate buttons dynamically.
final actions = [
'Add',
'Edit',
'Delete',
'Share',
];
Column(
children: actions.map((action) {
return ElevatedButton(
onPressed: () {
print(action);
},
child: Text(action),
);
}).toList(),
)
19. Dynamic UI with Icons
You can store icon information along with labels.
final menuItems = [
{
'title': 'Home',
'icon': Icons.home,
},
{
'title': 'Profile',
'icon': Icons.person,
},
{
'title': 'Settings',
'icon': Icons.settings,
},
];
Build the menu dynamically:
ListView.builder(
itemCount: menuItems.length,
itemBuilder: (context, index) {
final item = menuItems[index];
return ListTile(
leading: Icon(item['icon'] as IconData),
title: Text(item['title'] as String),
);
},
)
20. Handling User Interaction
Dynamic list items can respond to taps, buttons, gestures, and other events.
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
onTap: () {
print('Selected: ${product.name}');
},
);
},
)
21. Passing Selected Data to Another Screen
A common pattern is to pass the selected List item to a detail screen. Flutter's navigation recipes demonstrate passing an object from a dynamically generated list item to another screen. :contentReference[oaicite:3]{index=3}
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailPage(
product: product,
),
),
);
},
);
},
)
Detail Screen
class ProductDetailPage extends StatelessWidget {
final Product product;
const ProductDetailPage({
super.key,
required this.product,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(product.name),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text(product.category),
const SizedBox(height: 12),
Text('₹${product.price}'),
],
),
),
);
}
}
22. Adding Items Dynamically
A StatefulWidget can maintain a List and add new items when the user performs an action.
class TodoPage extends StatefulWidget {
const TodoPage({super.key});
@override
State createState() => _TodoPageState();
}
class _TodoPageState extends State {
final List todos = [];
void addTodo() {
setState(() {
todos.add(
'Task ${todos.length + 1}',
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo List'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index]),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: addTodo,
child: const Icon(Icons.add),
),
);
}
}
23. Removing Items Dynamically
You can remove an item from the underlying List and call setState() so the UI reflects the updated data.
void removeTodo(int index) {
setState(() {
todos.removeAt(index);
});
}
Use it in the UI:
ListTile(
title: Text(todos[index]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
removeTodo(index);
},
),
)
24. Swipe-to-Delete with Dismissible
Flutter's Dismissible widget can be combined with a dynamic List to allow users to swipe items away. The Flutter cookbook demonstrates removing an item from the data source after a swipe. :contentReference[oaicite:4]{index=4}
ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos[index];
return Dismissible(
key: ValueKey(todo),
onDismissed: (direction) {
setState(() {
todos.removeAt(index);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('$todo removed'),
),
);
},
child: ListTile(
title: Text(todo),
),
);
},
)
25. Updating an Existing Item
You can update the data in a List and rebuild the UI.
void updateProduct(int index) {
setState(() {
products[index] = Product(
name: products[index].name,
price: products[index].price + 1000,
category: products[index].category,
);
});
}
For larger applications, immutable model patterns can make updates easier to reason about.
26. Dynamic Checkbox List
A List can be used to generate multiple checkboxes.
final List skills = [
'Dart',
'Flutter',
'Firebase',
'Git',
];
final Set selectedSkills = {};
ListView.builder(
itemCount: skills.length,
itemBuilder: (context, index) {
final skill = skills[index];
return CheckboxListTile(
title: Text(skill),
value: selectedSkills.contains(skill),
onChanged: (value) {
setState(() {
if (value == true) {
selectedSkills.add(skill);
} else {
selectedSkills.remove(skill);
}
});
},
);
},
)
27. Dynamic Radio Button List
final paymentMethods = [
'Cash',
'Card',
'UPI',
];
String selectedPayment = 'Cash';
ListView.builder(
itemCount: paymentMethods.length,
itemBuilder: (context, index) {
final method = paymentMethods[index];
return RadioListTile(
title: Text(method),
value: method,
groupValue: selectedPayment,
onChanged: (value) {
setState(() {
selectedPayment = value!;
});
},
);
},
)
28. Dynamic Dropdown Data
Collections can also provide options for dropdown menus.
final cities = [
'Mumbai',
'Delhi',
'Pune',
'Bangalore',
];
String selectedCity = 'Mumbai';
DropdownButton(
value: selectedCity,
items: cities.map((city) {
return DropdownMenuItem(
value: city,
child: Text(city),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedCity = value!;
});
},
)
29. Dynamic Grid UI from Lists
The same data-driven approach can be used to build grid-based interfaces.
final categories = [
'Mobiles',
'Laptops',
'Tablets',
'Accessories',
];
GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: categories.length,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text(categories[index]),
),
);
},
)
30. Dynamic Horizontal List
For horizontally scrolling data, set scrollDirection to Axis.horizontal.
final categories = [
'All',
'Electronics',
'Books',
'Furniture',
];
SizedBox(
height: 60,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8),
child: Chip(
label: Text(categories[index]),
),
);
},
),
)
31. Dynamic Mixed-Type Lists
Sometimes an application needs a List containing different types of content, such as headings followed by messages. Flutter's documentation demonstrates defining different item types and converting them into widgets using ListView.builder. :contentReference[oaicite:5]{index=5}
abstract class ListItem {}
class HeadingItem implements ListItem {
final String heading;
HeadingItem(this.heading);
}
class MessageItem implements ListItem {
final String sender;
final String message;
MessageItem(this.sender, this.message);
}
Create the data:
final List items = [
HeadingItem('Messages'),
MessageItem('Amit', 'Hello!'),
MessageItem('Neha', 'How are you?'),
HeadingItem('Older Messages'),
MessageItem('Rahul', 'See you tomorrow.'),
];
Build different widgets based on the item type:
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
if (item is HeadingItem) {
return ListTile(
title: Text(
item.heading,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
);
}
if (item is MessageItem) {
return ListTile(
title: Text(item.sender),
subtitle: Text(item.message),
);
}
return const SizedBox.shrink();
},
)
32. Handling Empty Lists
A dynamic UI should also handle the case where there is no data.
body: products.isEmpty
? const Center(
child: Text('No products available'),
)
: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].name),
);
},
)
This creates a better user experience because the application does not show an empty screen without explanation.
33. Loading State Before Data Arrives
When collection data comes from an API or database, the UI may need to show a loading state before the List is populated.
bool isLoading = true;
List products = [];
Widget buildContent() {
if (isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (products.isEmpty) {
return const Center(
child: Text('No products found'),
);
}
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index]),
);
},
);
}
34. Error State for Dynamic Data
A production application should also consider the possibility that data loading fails.
bool isLoading = false;
String? errorMessage;
List products = [];
Widget buildContent() {
if (isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (errorMessage != null) {
return Center(
child: Text(errorMessage!),
);
}
if (products.isEmpty) {
return const Center(
child: Text('No products found'),
);
}
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index]),
);
},
);
}
35. ListView.builder vs ListView
| Feature | ListView | ListView.builder |
| Best for | Small, known collections | Dynamic or large collections |
| Child creation | Children are supplied directly | Children are generated by a builder |
| Large data | Less suitable | Designed for efficient scrolling of large lists |
| Dynamic data | Possible | Very convenient |
| Common usage | Static/simple lists | Products, messages, API data |
Flutter's long-list recipe specifically recommends ListView.builder for lists containing large numbers of items because items are built as they scroll into view. :contentReference[oaicite:6]{index=6}
36. Using itemExtent and prototypeItem
If list items have a predictable size, properties such as itemExtent or prototypeItem can provide Flutter with information about item dimensions. Flutter documents these options as ways to help scrolling layout when item extents are known. :contentReference[oaicite:7]{index=7}
ListView.builder(
itemCount: products.length,
itemExtent: 70,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].name),
);
},
)
37. Avoid Creating Widgets Before They Are Needed
For large collections, avoid manually converting thousands of data objects into widgets before displaying them.
Instead of:
final widgets = products.map((product) {
return ListTile(
title: Text(product.name),
);
}).toList();
ListView(
children: widgets,
)
prefer a builder:
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
);
},
)
This builder pattern is particularly useful for long lists because Flutter can construct list items on demand. :contentReference[oaicite:8]{index=8}
38. Keeping Data Separate from UI
A useful architecture is to keep data in model classes and let widgets focus on presentation.
class User {
final String name;
final String email;
const User({
required this.name,
required this.email,
});
}
class UserList extends StatelessWidget {
final List users;
const UserList({
super.key,
required this.users,
});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
);
},
);
}
}
This makes the widget reusable with different user collections.
39. Reusable Dynamic List Widget
You can create a reusable widget that accepts a List and a builder function.
class DynamicList extends StatelessWidget {
final List items;
final Widget Function(BuildContext, T) itemBuilder;
const DynamicList({
super.key,
required this.items,
required this.itemBuilder,
});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return itemBuilder(
context,
items[index],
);
},
);
}
}
Usage:
DynamicList(
items: const [
'Apple',
'Banana',
'Mango',
],
itemBuilder: (context, fruit) {
return ListTile(
title: Text(fruit),
);
},
)
40. Complete Practical Todo Application
import 'package:flutter/material.dart';
void main() {
runApp(const TodoApp());
}
class TodoApp extends StatelessWidget {
const TodoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const TodoPage(),
);
}
}
class TodoPage extends StatefulWidget {
const TodoPage({super.key});
@override
State createState() => _TodoPageState();
}
class _TodoPageState extends State {
final List todos = [
'Learn Dart',
'Learn Flutter',
'Build a project',
];
void addTodo() {
setState(() {
todos.add(
'Task ${todos.length + 1}',
);
});
}
void removeTodo(int index) {
setState(() {
todos.removeAt(index);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dynamic Todo List'),
),
body: todos.isEmpty
? const Center(
child: Text('No tasks available'),
)
: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos[index];
return ListTile(
leading: CircleAvatar(
child: Text(
'${index + 1}',
),
),
title: Text(todo),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
removeTodo(index);
},
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: addTodo,
child: const Icon(Icons.add),
),
);
}
}
41. Data Flow in Dynamic UI
A typical dynamic Flutter UI follows this flow:
- Create or receive the data.
- Store the data in a List or another collection.
- Pass the collection to the widget.
- Use
ListView.builder, GridView.builder, or another builder to generate widgets.
- Use the current index or object to display the correct data.
- Handle user interaction such as taps, edits, or deletion.
- Update the underlying data.
- Rebuild the UI when the application state changes.
Data
↓
List / Collection
↓
Builder Widget
↓
Individual Data Item
↓
Flutter Widget
↓
User Interaction
↓
Updated Data
↓
UI Rebuild
42. Common Mistakes
- Using the wrong List index.
- Forgetting to set
itemCount correctly.
- Trying to access data after the List has changed without considering the current index.
- Creating very large numbers of widgets manually.
- Forgetting to call
setState() when local StatefulWidget data changes.
- Using
shrinkWrap: true unnecessarily for large lists.
- Using ListView inside another scrollable widget without understanding nested scrolling.
- Displaying complex API data using untyped Maps throughout a large application instead of model classes.
- Not handling empty, loading, and error states.
- Using unstable or inappropriate keys for stateful dynamic list items.
43. Best Practices
- Keep application data separate from presentation code.
- Use strongly typed model classes for complex data.
- Use
ListView.builder for large or dynamically generated lists.
- Use
GridView.builder when the UI is grid-based.
- Use
setState() for local state changes in StatefulWidgets.
- Handle loading, empty, success, and error states when data comes from external sources.
- Use keys appropriately when list items contain stateful widgets.
- Use filtering and mapping operations to prepare data before presenting it.
- Keep item widgets small and reusable when list rows become complex.
- Use appropriate item extent information when list item sizes are known.
44. Practice Exercises
- Create a List of 10 student names and display them using
ListView.builder.
- Create a List of Product objects and display product name, category, and price.
- Create a dynamic contact list with name, phone number, and email.
- Create a horizontal category list using
ListView.builder.
- Create a search field that filters a product List dynamically.
- Create a todo list where users can add and delete tasks.
- Add swipe-to-delete functionality using
Dismissible.
- Create a dynamic checkbox list of programming skills.
- Create a dynamic grid of product categories using
GridView.builder.
- Create a product list where tapping an item opens a detail screen.
- Create loading and empty states for a dynamic List.
- Create a mixed list containing headings and messages.
45. Quick Revision
- Dynamic UI: UI generated from application data.
- List: Stores multiple ordered values.
- map(): Converts data items into another representation.
- ListView: Displays a scrollable list.
- ListView.builder: Builds list items on demand.
- itemCount: Specifies the number of items.
- itemBuilder: Generates the widget for an item.
- index: Identifies the current position in the List.
- setState(): Rebuilds a StatefulWidget after local state changes.
- Dismissible: Enables swipe-based dismissal of list items.
- GridView.builder: Generates grid-based UI dynamically.
- Model class: Provides structured and strongly typed application data.
46. Conclusion
Building Dynamic UI from Lists is a fundamental Flutter development technique. The basic idea is simple: store application data in a collection and use Flutter widgets to convert that data into UI elements. For small collections, ListView or collection-to-widget transformations can be appropriate, while ListView.builder is particularly useful for dynamic and large lists because it builds children as they become necessary. :contentReference[oaicite:9]{index=9}
This approach can be extended to products, users, messages, search results, categories, todos, API responses, grids, forms, navigation, and interactive application screens.
Official Flutter Resources
Learn Flutter with JustAcademy